from cgi import escape
from random import uniform
from Queue import Queue,Empty
from sets import Set
from socket import error
from threading import Thread
from urllib import unquote_plus


class Connection(Queue):
    """Handles the persistant connection between the client and server"""
    #I don't think Set() is thread safe.
    objects=Set() #set of live connections

    def __init__(self,obj_up_hook=None):
        self.name=""
        self.obj_up_hook=obj_up_hook
        Queue.__init__(self)

    def __str__(self):
        return self.name

    def __repr__(self):
        return "Connection object: " + str(self)

    def online(self):
        self.objects.add(self)
        print '"' + str(self) + '" has joined'
        if self.obj_up_hook:
            self.obj_up_hook(self)

    def offline(self):
        self.objects.discard(self)
        print '"' + str(self) + '" has left'
        if self.obj_up_hook:
            self.obj_up_hook(self)

    def send_to_all(msg):
        """Sends a message to all online objects"""
        for x in Connection.objects:
            x.put(msg)
            
    send_to_all = staticmethod(send_to_all)

    def run(self,write,keep_alive=" "):
        """Waits for messages and outputs them until window is closed"""
        self.online()
        while 1:
            try:
                #Wait for a new message.
                m=self.get(True,uniform(5,9))
            except Empty:
                #The waiting timed out.
                m=keep_alive
            try:
                write(m)
            except error:
                #most likely the client closed the window
                self.offline()
                return

class ChatApp():
    """Handles a Request"""
    def __init__(self, environ, start_response):
        self.environ=environ
        self.start_response=start_response

    def index(self):
        """login page"""
        return """<script>
function submit(e){
 if(!e)e=window.event;
 if(e.keyCode==13){
  url="/main/"+input.value;
  location.href=url
 }
}
</script>
<body>
<table width=100% height=60%>
<td width=100% height=100%><center>Enter your name:<br><input id=input style="width:50%" onkeypress="submit(event)">"""

    def main(self,user):
        """main page"""
        return '''<script>
function submit(e){
 if(!e)e=window.event;
 if(e.keyCode==13){
  url="/ajax/'''+str(user)+'''?"+input.value;
  input.value="";
  if(window.ActiveXObject){ajax=new ActiveXObject("Microsoft.XMLHTTP")};
  if(window.XMLHttpRequest){ajax=new XMLHttpRequest()};
  ajax.open("GET",url,true);
  ajax.send(null);
 }
}
</script>
<body topmargin=0 bottommargin=0 leftmargin=0 rightmargin=0>
<table width=100% height=100% cellspacing=0 cellpadding=0>
<td width=80% height=100%>
<iframe id=thebox style="border-right:0;border-left:0;border-top:0;border-bottom:0" width=100% height=100% src="/top/'''+str(user)+'''"></iframe>
<td width=20% height=100%>
<iframe name=thelist style="border-right:0;border-left:0;border-top:0;border-bottom:0" width=100% height=100% src="/list/'''+str(user)+'''"></iframe>
<tr><td><input id=input style="width:100%" value="Type your message here" onkeypress="submit(event)">'''

    def refresh_online_list(self,connection):
        Connection.send_to_all('<script>u()</script>')
        
    def top(self,write,usern):
        """actual chat window"""
        write("""<body>You are now in the chat room.<br><script>
function u(){parent.frames["thelist"].location.reload();}
function s(str){document.write(str);window.scrollBy(0,100);}
</script>""")
        u=Connection(self.refresh_online_list)
        u.name=usern
        u.run(write)

    def onlinelist(self,user):
        """online list"""
        string =  "<b>" + str(len(Connection.objects)) + " Online:</b><br>"
        for u in Connection.objects:
            string += str(u)+"<br>\n"
        return string

    def ajax(self,user,message):
        """page that accepts messages"""
        #this escape function escapes all html and quotes
        print "recieved message: " + message
        print "sending message: " + self.esc(message)
        Connection.send_to_all("<script>s('" + "<b>" + self.esc(str(user)) + \
                ":</b> " + self.esc(message) + "<br>" + "')</script>")
        print "done"

    def esc(string):
        #for html:
        string = escape(string,True)
        #for javascript (order is important)
        string = string.replace("\\","\\\\")
        string = string.replace("'","\\'")
        return string

    esc = staticmethod(esc)

    def __iter__(self):
        print "recieved request"
        write = self.start_response('200 OK', [('Content-type', 'text/html')])
        print self.environ['PATH_INFO']

        patharray = self.environ['PATH_INFO'].split('/')
        if patharray==["",""]:
            yield self.index()
            return
        if patharray[1]=="favicon.ico":
            return
        command=patharray[1]
        user=patharray[2]
        if command=="main":
            yield self.main(user)
        elif command=="top":
            self.top(write,user)
            yield ""
        elif command=="list":
            yield self.onlinelist(user)
        elif command=="ajax":
            self.ajax(user,unquote_plus(self.environ['QUERY_STRING']))
            yield ""
        else:
            yield "unknown command: "+str(command)

if __name__ == '__main__':
    from flup.server.fcgi import WSGIServer
    print "FCGI server running"
    httpd = WSGIServer(ChatApp, bindAddress="/var/run/django.sock").run()
    startfcgi()

